package com.face.server.communication.mina.handlers;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.face.server.common.domain.ByteArrayReceiverEx;
import com.face.server.common.domain.entity.*;
import com.face.server.common.utils.log.Logs;
import com.face.server.communication.mina.cache.MinaCache;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.slf4j.Logger;
import org.springframework.stereotype.Component;

import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.Date;

/**
 * @author yangzhongming
 * @desc 【人脸识别出纸机】--消息处理handler
 * @date 2022/1/24
 */
@Component
public class MinaServerHandler extends IoHandlerAdapter {
    /**
     * 日志记录
     */
    private static final Logger logger = Logs.get();


    /**
     * 连接打开
     */
    @Override
    public void sessionOpened(IoSession session) {
//        String msg = "新模块客户端链接进入，当前在线客户端数：" + session.getService().getManagedSessionCount();
//        logger.info(msg);
    }

    /* 连接关闭*/
    @Override
    public void sessionClosed(IoSession session) {
        if (session != null && session.getAttribute("name") != null) {
            close((String) session.getAttribute("name"), session);
        }
        if (session != null)
            session.closeNow();


    }


    @Override
    public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
        // todo 可处理空闲断开
//        if (status == IdleStatus.BOTH_IDLE) {
//            // 连接超时
//            session.closeNow();
//            logger.info("客户端【" + (session.getAttribute("name") == null ? "" : session.getAttribute("name")) + "】超时");
//        }
    }

    @Override
    public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
        if (session.getAttribute("name") != null) {
            logger.error("客户端【" + session.getAttribute("name") + "】发生异常", cause);
            close((String) session.getAttribute("name"), session);
        } else {
            logger.error("未知客户端发生异常", cause);
        }
        session.closeNow();
        cause.printStackTrace();
    }

    @Override
    public void messageReceived(IoSession session, Object message) throws Exception {
        if (session.getAttribute("name") != null) {
            String token = (String) session.getAttribute("name");
            MinaCache.map.put(token, session);
        }
        ByteArrayReceiverEx byteArrayReceiverEx = (ByteArrayReceiverEx) message;
        int order = byteArrayReceiverEx.getOrder() & 0xFF;
        System.out.println(LocalDateTime.now() + ">>收到消息：header: " + order + " content: " + new String(byteArrayReceiverEx.getContent()));
        switch (order) {
            case 0x88:
                //心跳包
//                logger.info("收到心跳包：" + session.getAttribute("name"));
                JSONObject jsonObject = JSON.parseObject(new String(byteArrayReceiverEx.getContent()));
                String name = null;
                if (jsonObject.containsKey("token")) {
                    name = jsonObject.getString("token");
                } else {
                    name = (String) session.getAttribute("name");
                }
                // todo 心跳包 处理，可根据自己规定的时间，超过多少时间，则表示该机器断线
                // todo 机器时常发送心跳包，也能确认机器在线，否则不在线，长时间不在线，需重启机器

                break;
            case 0xFF:
                //登录消息
                String token1 = new String(byteArrayReceiverEx.getContent());
                // 需通过该token 查询 数据库中机器信息；该案例中不展示数据库操作，仅仅提示
                // todo 该操作实际应该应该查询数据库，这里仅作方法演示
                PaperDevice paperDevice1 = PaperDevice.getPaperDeviceByToken(token1);
                // 根据token查询设备，并修改状态
                if (paperDevice1 == null) {
                    //不存在该设备---断开连接(不要 session.closeNow(); 会出现疯狂连接，导致Android端OOM)
                    logger.error("【人脸识别出纸机】非法设备连接服务器：token:" + new String(byteArrayReceiverEx.getContent()));
                    session.closeNow();
                } else {
                    session.setAttribute("name", paperDevice1.getToken());
                    MinaCache.map.put(paperDevice1.getToken(), session);
                    // todo 修改数据库中，对应机器的在线状态

                }
                break;
            case 0x01:
                //日志信息
                if (session.getAttribute("name") != null) {
                    String token2 = (String) session.getAttribute("name");
                    // todo 该操作实际应该应该查询数据库，这里仅作方法演示
                    PaperDevice paperDevice2 = PaperDevice.getPaperDeviceByToken(token2);
                    if (paperDevice2 != null) {
                        String data = new String(byteArrayReceiverEx.getContent());
                        try {
                            JSONObject logInfoData = JSON.parseObject(data);
                            PaperDeviceLog paperDeviceLog = new PaperDeviceLog();
                            paperDeviceLog.setContent(logInfoData.getString("content"));
                            paperDeviceLog.setLogLevel(logInfoData.getString("level"));
                            // todo 这里获取的时间为机器上的时间--机器上的时间是不准确的
                            paperDeviceLog.setTime(logInfoData.getDate("time"));
                            paperDeviceLog.setDeviceId(paperDevice2.getId());
                            // todo 日志实际发生时间，用接收到时间
                            paperDeviceLog.setCreated(new Date());
                            // todo 看情况入库与否
                        } catch (Exception e) {
                            logger.error("【人脸识别出纸机】解析数据错误，【协议：01】，【原因：" + e.getMessage() + "】");
                            e.printStackTrace();
                        }
                    }
                } else {
                    session.closeNow();
                }
                break;
            case 0x02:
                if (session.getAttribute("name") != null) {
                    String token3 = (String) session.getAttribute("name");
                    // todo 该操作实际应该应该查询数据库，这里仅作方法演示
                    PaperDevice paperDevice3 = PaperDevice.getPaperDeviceByToken(token3);
                    if (paperDevice3 != null) {
                        String data = new String(byteArrayReceiverEx.getContent());
                        try {
                            JSONObject deviceStatusData = JSON.parseObject(data);
//                            logger.info(paperTakingDevice1.getSerialNum() + "【人脸识别出纸机】上传数据：【协议：02】，【data:" + deviceStatusData + "】");
                            PaperDeviceStatus paperDeviceStatus = new PaperDeviceStatus();
                            paperDeviceStatus.setPaperTakingDeviceId(paperDevice3.getId());
                            paperDeviceStatus.setPaperStatus(deviceStatusData.getInteger("paperStatus"));
                            paperDeviceStatus.setSideDoorStatus(deviceStatusData.getInteger("sideDoorStatus"));
                            paperDeviceStatus.setPaperAllowance(deviceStatusData.getInteger("paperAllowance"));
                            paperDeviceStatus.setCreated(Timestamp.valueOf(LocalDateTime.now()));
                            // todo 看情况入库
                        } catch (Exception e) {
                            logger.error("解析数据错误，【协议：02】，【原因：" + e.getMessage() + "】");
                            e.printStackTrace();
                        }
                    }
                } else {
                    session.closeNow();
                }
                break;
            case 0x03:
                if (session.getAttribute("name") != null) {
                    String token4 = (String) session.getAttribute("name");
                    // todo 该操作实际应该应该查询数据库，这里仅作方法演示,这里获取的对象永远不为空
                    PaperDevice paperDevice4 = PaperDevice.getPaperDeviceByToken(token4);
                    if (paperDevice4 != null) {
                        String data = new String(byteArrayReceiverEx.getContent());
                        try {
                            JSONObject paperOutLogData = JSON.parseObject(data);
//                            logger.info(paperTakingDevice1.getSerialNum() + "【人脸识别出纸机】上传数据：【协议：03】，【data:" + paperOutLogData + "】");
                            String advertToken = paperOutLogData.getString("advertToken");
                            PaperAdvert advert = null;
                            if (!"default".equals(advertToken)) {
                                // todo 该操作实际应该应该查询数据库，这里仅作方法演示,这里获取的对象永远不为空
                                advert = PaperAdvert.getAdvertByToken(advertToken);
                                if (advert == null) {
                                    PaperDeviceLog paperDeviceLog = new PaperDeviceLog();
                                    paperDeviceLog.setContent("非法广告正在播放【平台不存在该广告的token信息】");
                                    paperDeviceLog.setCreated(new Date());
                                    paperDeviceLog.setDeviceId(paperDevice4.getId());
                                    paperDeviceLog.setLogLevel("ERROR");
                                    // todo 视情况入库与否
                                }
                            }
                            PaperOutLog paperOutLog = new PaperOutLog();
                            paperOutLog.setDeviceId(paperDevice4.getId());
                            paperOutLog.setStatus(paperOutLogData.getInteger("status"));
                            paperOutLog.setPaperLength(paperOutLogData.getInteger("length"));
                            paperOutLog.setAdvertId(advert == null ? -1 : advert.getId());
                            // todo 这里获取的时间为机器上的时间--机器上的时间是不准确的
                            paperOutLog.setTime(paperOutLogData.getDate("time"));
                            // todo 使用这里接收到消息的时间
                            paperOutLog.setCreated(Timestamp.valueOf(LocalDateTime.now()));
                            // todo 视情况入库
                        } catch (Exception e) {
                            logger.error("【人脸识别出纸机】解析数据错误，【协议：03】，【原因：" + e.getMessage() + "】");
                            e.printStackTrace();
                        }
                    }
                } else {
                    session.closeNow();
                }
            case 0x04:
                if (session.getAttribute("name") != null) {
                    String token5 = (String) session.getAttribute("name");
                    // todo 该操作实际应该应该查询数据库，这里仅作方法演示,这里获取的对象永远不为空
                    PaperDevice paperDevice5 = PaperDevice.getPaperDeviceByToken(token5);
                    if (paperDevice5 != null) {
                        String data = new String(byteArrayReceiverEx.getContent());
                        try {
                            JSONObject paperOutLogData = JSON.parseObject(data);
//                            logger.info(paperTakingDevice1.getSerialNum() + "【人脸识别出纸机】下发广告成功：【协议：04】，【data:" + paperOutLogData + "】");
                            String advertToken = paperOutLogData.getString("token");
                            if (advertToken != null && !"".equals(advertToken)) {
                                // todo 接收到该消息说明对应token 的广告已收到，可以更新数据库状态
                            }

                        } catch (Exception e) {
                            logger.error("【人脸识别出纸机】解析数据错误，【协议：04】，【原因：" + e.getMessage() + "】");
                            e.printStackTrace();
                        }
                    }
                } else {
                    session.closeNow();
                }
                break;
            case 0x05:
                if (session.getAttribute("name") != null) {
                    String token5 = (String) session.getAttribute("name");
                    // todo 该操作实际应该应该查询数据库，这里仅作方法演示,这里获取的对象永远不为空
                    PaperDevice paperDevice5 = PaperDevice.getPaperDeviceByToken(token5);
                    if (paperDevice5 != null) {
                        String data = new String(byteArrayReceiverEx.getContent());
                        try {
                            JSONObject paperOutLogData = JSON.parseObject(data);
//                            logger.info(paperTakingDevice1.getSerialNum() + "【人脸识别出纸机】下发广告成功：【协议：04】，【data:" + paperOutLogData + "】");
                            String advertToken = paperOutLogData.getString("token");
                            if (advertToken != null && !"".equals(advertToken)) {
                                // 该广告已删除
                            }
                            String currentAdverts = paperOutLogData.getString("currentAdverts");
                            // todo 该字段可用于机器现有播放列表，可用于数据验证

                        } catch (Exception e) {
                            logger.error("【人脸识别出纸机】解析数据错误，【协议：04】，【原因：" + e.getMessage() + "】");
                            e.printStackTrace();
                        }
                    }
                } else {
                    session.closeNow();
                }
                break;

        }
    }

    public void close(String name) {
        if (MinaCache.map.containsKey(name)) {
            IoSession session = MinaCache.map.get(name);
            session.closeNow();
        }
    }

    public void close(String name, IoSession session) {
        if (name == null) return;
        PaperDevice paperDevice = PaperDevice.getPaperDeviceByToken(name);
        if (paperDevice != null) {
            // todo 修改数据库机器状态 - 离线
        }
        session.removeAttribute("name");
        session.closeNow();
        MinaCache.map.remove(name);
    }
}
